package avicit.bdp.oss.service.stats;

import avicit.bdp.common.base.BaseService;
import avicit.bdp.oss.dao.BucketAuditDao;
import avicit.bdp.oss.dto.BucketAuditDto;
import avicit.bdp.oss.dto.BucketDto;
import avicit.bdp.oss.service.bucket.BucketService;
import avicit.bdp.oss.utils.common.AcitonEnum;
import avicit.bdp.oss.utils.common.Constants;
import avicit.bdp.oss.utils.common.TimeUtils;
import avicit.bdp.oss.utils.stats.BucketMetricStats;
import avicit.bdp.oss.utils.stats.MathUtils;
import avicit.bdp.oss.utils.stats.ReqPara;
import avicit.bdp.oss.utils.stats.TrafficPara;
import avicit.platform6.core.exception.BusinessException;
import com.alibaba.fastjson2.JSONObject;
import com.github.pagehelper.Page;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2020-11-12
 * @类说明：BucketAuditService
 * @修改记录：
 * @注意事项：
 * @功能：获取指标统计信息，然后持久化到DB中
 */
@Service
@Transactional
public class BucketAuditService extends BaseService<BucketAuditDao, BucketAuditDto> {
    private static final Logger logger = LoggerFactory.getLogger(BucketAuditService.class);

    /**
     * Dao层
     */
    @Autowired
    private BucketAuditDao bucketAuditDao;

    @Autowired
    private BucketService bucketService;

    /**
     * 执行定时查询bucket指标统计信息
     */
    public void runScheduledTask() {
        int pageNum = 1;

        // step1:获取bucket列表
        Page<BucketDto> bucketDtos = this.bucketService.pageQueryBucket(pageNum++);
        updateBucketMetricStats(bucketDtos.getResult());

        // step2:获取分页总数
        long totalPages = bucketDtos.getPages();

        // step3:根据分页总数，循环遍历指标信息，持久化
        for (; pageNum <= totalPages; pageNum++) {
            bucketDtos = this.bucketService.pageQueryBucket(pageNum);
            updateBucketMetricStats(bucketDtos.getResult());
        }
    }

    /**
     * 获取指定bucketId指标统计信息
     * @param bucketId
     * @return
     */
    public BucketMetricStats getBucketMetricStats(String bucketId) {
        BucketMetricStats bucketMetricStats = new BucketMetricStats();

        bucketMetricStats.setMonthReadCapacityStats(getMonthReadCapacityStats(bucketId));
        bucketMetricStats.setMonthWriteCapacityStats(getMonthWriteCapacityStats(bucketId));
        bucketMetricStats.setMonthGetReqStats(getMonthGetReqStats(bucketId));
        bucketMetricStats.setMonthPutReaStats(getMonthPutReqStats(bucketId));
        bucketMetricStats.setTodayReadCapacityStats(getTodayReadCapacityStats(bucketId));
        bucketMetricStats.setTodayWriteCapacityStats(getTodayWriteCapacityStats(bucketId));
        bucketMetricStats.setTodayGetReqStats(getTodayGetReqStats(bucketId));
        bucketMetricStats.setTodayPutReqStats(getTodayPutReqStats(bucketId));

        return bucketMetricStats;
    }

    /**
     * 从数据库中解析审计日志，获取本月读取量
     * @param bucketId
     * @return
     */
    public BucketMetricStats.MonthReadCapacityStats getMonthReadCapacityStats(String bucketId) {
        // step1:获取本月读取量
        long totalCapacity = singleQureyMetric(bucketId, QueryTypeEnum.MONTH_READ);

        // step2:获取今日读取量
        long dailyCapacity = singleQureyMetric(bucketId, QueryTypeEnum.DAILY_READ);

        // step3:获取前一日读取量
        long lastDayReadCapacity = singleQureyMetric(bucketId, QueryTypeEnum.LAST_DAY_READ);

        // step4:获取近7日读取量
        List<TrafficPara> recentWriteCapacity = batchQureyMetric(bucketId, QueryTypeEnum.RECENT_DAYS_READ);

        // step5:返回处理后结果
        return new BucketMetricStats.MonthReadCapacityStats(
                MathUtils.storageUnitProcess(totalCapacity),
                MathUtils.storageUnitProcess(dailyCapacity),
                MathUtils.calRatio(lastDayReadCapacity, dailyCapacity),
                recentWriteCapacity);
    }

    /**
     * 从数据库中解析审计日志，获取本月写入量
     * @param bucketId
     * @return
     */
    public BucketMetricStats.MonthWriteCapacityStats getMonthWriteCapacityStats(String bucketId) {
        // step1:获取本月写入量
        long totalCapacity = singleQureyMetric(bucketId, QueryTypeEnum.MONTH_WRITE);

        // step2:获取今日写入量
        long dailyCapacity = singleQureyMetric(bucketId, QueryTypeEnum.DAILY_WRITE);

        // step3:获取前一日写入量
        long lastDayWriteCapacity = singleQureyMetric(bucketId, QueryTypeEnum.LAST_DAY_WRITE);

        // step4:获取近7日写入量
        List<TrafficPara> recentWriteCapacity = batchQureyMetric(bucketId, QueryTypeEnum.RECENT_DAYS_WRITE);

        // step5:返回处理后结果
        return new BucketMetricStats.MonthWriteCapacityStats(
                MathUtils.storageUnitProcess(totalCapacity),
                MathUtils.storageUnitProcess(dailyCapacity),
                MathUtils.calRatio(lastDayWriteCapacity, dailyCapacity),
                recentWriteCapacity);
    }

    /**
     * 从数据库中解析审计日志，获取本月GET请求数
     * @param bucketId
     * @return
     */
    public BucketMetricStats.MonthGetReqStats getMonthGetReqStats(String bucketId) {
        // step1:获取本月GET请求数
        long totalGetNum = singleQureyMetric(bucketId, QueryTypeEnum.MONTH_GET);

        // step2:获取今日GET请求数
        long dailyGetNum = singleQureyMetric(bucketId, QueryTypeEnum.DAILY_GET);

        // step3:获取前一日GET请求数
        long lastDayGetNum = singleQureyMetric(bucketId, QueryTypeEnum.LAST_DAY_GET);

        // step4:获取近7日GET请求数
        List<TrafficPara> trafficParas = batchQureyMetric(bucketId, QueryTypeEnum.RECENT_DAYS_GET);
        List<ReqPara> recentGetNum = chgTraffic2ReqPara(trafficParas);

        // step5:返回处理后结
        return new BucketMetricStats.MonthGetReqStats(
                totalGetNum, dailyGetNum,
                MathUtils.calRatio(lastDayGetNum, dailyGetNum),
                recentGetNum);
    }

    /**
     * 从数据库中解析审计日志，获取本月PUT请求数
     * @param bucketId
     * @return
     */
    public BucketMetricStats.MonthPutReqStats getMonthPutReqStats(String bucketId) {
        // step1:获取本月PUT请求数
        long totalPutNum = singleQureyMetric(bucketId, QueryTypeEnum.MONTH_PUT);

        // step2:获取今日PUT请求数
        long dailyPutNum = singleQureyMetric(bucketId, QueryTypeEnum.DAILY_PUT);

        // step3:获取前一日PUT请求数
        long lastDayPutNum = singleQureyMetric(bucketId, QueryTypeEnum.LAST_DAY_PUT);

        // step4:获取近7日PUT请求数
        List<TrafficPara> trafficParas = batchQureyMetric(bucketId, QueryTypeEnum.RECENT_DAYS_PUT);
        List<ReqPara> recentPutNum = chgTraffic2ReqPara(trafficParas);

        // step5:返回处理后结果
        return new BucketMetricStats.MonthPutReqStats(
                totalPutNum,
                dailyPutNum,
                MathUtils.calRatio(lastDayPutNum, dailyPutNum),
                recentPutNum);
    }

    /**
     * 从数据库中解析审计日志，获取今日读取量
     * @param bucketId
     * @return
     */
    public BucketMetricStats.DailyReadCapacityStats getTodayReadCapacityStats(String bucketId) {
        // step1:获取今日最大读取量
        long maxReadCapacity = singleQureyMetric(bucketId, QueryTypeEnum.DAILY_MAX_READ);

        // step2:获取今日最小读取量
        long minReadCapacity = singleQureyMetric(bucketId, QueryTypeEnum.DAILY_MIN_READ);

        // step3:获取当前时间点近12h读取量
        List<TrafficPara> recentReadCapacity = batchQureyMetric(bucketId, QueryTypeEnum.RECENT_HOURS_READ);

        // step4:计算横轴（时间）
        List<Long> horizontalList = parseStampFromTrafficParas(recentReadCapacity);

        // step5:计算纵轴（读取量坐标）
        List<TrafficPara> verticalList = adjustVerticalOfTrafficPara(recentReadCapacity);

        // step6:根据纵轴坐标单位，处理读取量
        procCapacityWithUnit(recentReadCapacity, verticalList);

        // step7:返回处理结果
        return new BucketMetricStats.DailyReadCapacityStats(
                MathUtils.storageUnitProcess(maxReadCapacity),
                MathUtils.storageUnitProcess(minReadCapacity),
                horizontalList,
                verticalList,
                recentReadCapacity);
    }

    /**
     * 从数据库中解析审计日志，获取今日写入量
     * @param bucketId
     * @return
     */
    public BucketMetricStats.DailyWriteCapacityStats getTodayWriteCapacityStats(String bucketId) {
        // step1:获取今日最大写入量
        long maxWriteCapacity = singleQureyMetric(bucketId, QueryTypeEnum.DAILY_MAX_WRITE);

        // step2:获取今日最小写入量
        long minWriteCapacity = singleQureyMetric(bucketId, QueryTypeEnum.DAILY_MIN_WRITE);

        // step3:获取当前时间点近12h写入量
        List<TrafficPara> recentWriteCapacity = batchQureyMetric(bucketId, QueryTypeEnum.RECENT_HOURS_WRITE);

        // step4:计算横轴（时间）
        List<Long> horizontalList = parseStampFromTrafficParas(recentWriteCapacity);

        // step5:计算纵轴（读取量坐标）
        List<TrafficPara> verticalList = adjustVerticalOfTrafficPara(recentWriteCapacity);

        // step6:根据纵轴坐标单位，处理写入量
        procCapacityWithUnit(recentWriteCapacity, verticalList);

        // step6:返回处理结果
        return new BucketMetricStats.DailyWriteCapacityStats(
                MathUtils.storageUnitProcess(maxWriteCapacity),
                MathUtils.storageUnitProcess(minWriteCapacity),
                horizontalList,
                verticalList,
                recentWriteCapacity);
    }

    /**
     * 从数据库中解析审计日志，获取今日GET请求数
     * @param bucketId
     * @return
     */
    public BucketMetricStats.DailyGetReqStats getTodayGetReqStats(String bucketId) {
        // step1:获取当前时间点近12hGET请求数
        List<TrafficPara> trafficParas = batchQureyMetric(bucketId, QueryTypeEnum.RECENT_HOURS_GET);
        List<ReqPara> recentGetNum = chgTraffic2ReqPara(trafficParas);

        // step2:获取今日最大GET请求数
        long maxGetReq = getMaxParaFromReqParas(recentGetNum);

        // step3:获取今日最小GET请求数
        long minGetReq = getMinParaFromReqParas(recentGetNum);

        // step4:计算横轴（时间）
        List<Long> horizontalList = parseStampFromReqParas(recentGetNum);

        // step5:计算纵轴（读取量坐标）
        List<ReqPara> verticalList = adjustVerticalOfReqPara(recentGetNum);

        // step6:返回处理结果
        return new BucketMetricStats.DailyGetReqStats(
                maxGetReq,
                minGetReq,
                horizontalList,
                verticalList,
                recentGetNum);
    }

    /**
     * 从数据库中解析审计日志，获取今日PUT请求数
     * @param bucketId
     * @return
     */
    public BucketMetricStats.DailyPutReqStats getTodayPutReqStats(String bucketId) {
        // step1:获取当前时间点近12hPUT请求数
        List<TrafficPara> trafficParas = batchQureyMetric(bucketId, QueryTypeEnum.RECENT_HOURS_PUT);
        List<ReqPara> recentPutNum = chgTraffic2ReqPara(trafficParas);

        // step2:获取今日最大PUT请求数
        long maxPutReq = getMaxParaFromReqParas(recentPutNum);

        // step3:获取今日最小PUT请求数
        long minPutReq = getMinParaFromReqParas(recentPutNum);

        // step4:计算横轴（时间）
        List<Long> horizontalList = parseStampFromReqParas(recentPutNum);

        // step5:计算纵轴（读取量坐标）
        List<ReqPara> verticalList = adjustVerticalOfReqPara(recentPutNum);

        // step6:返回处理结果
        return new BucketMetricStats.DailyPutReqStats(
                maxPutReq,
                minPutReq,
                horizontalList,
                verticalList,
                recentPutNum);
    }

    /**
     * 更新指标信息
     * @param bucketDtos
     */
    private void updateBucketMetricStats(List<BucketDto> bucketDtos) {
        for (BucketDto tmpBucketDto : bucketDtos) {
            // step1:获取指定bucketId指标统计信息
            BucketMetricStats bucketMetricStats = getBucketMetricStats(tmpBucketDto.getId());

            // step2:bucket指标统计信息持久化
            String bucketMetricStatsStr = JSONObject.toJSONString(bucketMetricStats);
            tmpBucketDto.setBucketMetricStats(bucketMetricStatsStr);

            this.bucketService.updateByPrimaryKey(tmpBucketDto);
        }
    }

    /**
     * 定义查询类型枚举类
     */
    public static enum QueryTypeEnum {
        MONTH_READ(MetricTypeEnum.CAPACITY, AcitonEnum.GET_OBJECT),              // 本月读取量
        MONTH_WRITE(MetricTypeEnum.CAPACITY, AcitonEnum.PUT_OBJECT),             // 本月写入量
        MONTH_GET(MetricTypeEnum.REQ_NUM, AcitonEnum.GET_OBJECT),                // 本月GET请求数
        MONTH_PUT(MetricTypeEnum.REQ_NUM, AcitonEnum.PUT_OBJECT),                // 本月PUT请求数
        DAILY_READ(MetricTypeEnum.CAPACITY, AcitonEnum.GET_OBJECT),              // 今日读取量
        DAILY_WRITE(MetricTypeEnum.CAPACITY, AcitonEnum.PUT_OBJECT),             // 今日写入量
        DAILY_GET(MetricTypeEnum.REQ_NUM, AcitonEnum.GET_OBJECT),                // 今日GET请求数
        DAILY_PUT(MetricTypeEnum.REQ_NUM, AcitonEnum.PUT_OBJECT),                // 今日PUT请求数
        LAST_DAY_READ(MetricTypeEnum.CAPACITY, AcitonEnum.GET_OBJECT),           // 前一天读取量
        LAST_DAY_WRITE(MetricTypeEnum.CAPACITY, AcitonEnum.PUT_OBJECT),          // 前一天写入量
        LAST_DAY_GET(MetricTypeEnum.REQ_NUM, AcitonEnum.GET_OBJECT),             // 前一天GET请求数
        LAST_DAY_PUT(MetricTypeEnum.REQ_NUM, AcitonEnum.PUT_OBJECT),             // 前一天PUT请求数
        RECENT_DAYS_READ(MetricTypeEnum.CAPACITY, AcitonEnum.GET_OBJECT),        // 近7天读取量
        RECENT_DAYS_WRITE(MetricTypeEnum.CAPACITY, AcitonEnum.PUT_OBJECT),       // 近7天写入量
        RECENT_DAYS_GET(MetricTypeEnum.REQ_NUM, AcitonEnum.GET_OBJECT),          // 近7天GET请求数
        RECENT_DAYS_PUT(MetricTypeEnum.REQ_NUM, AcitonEnum.PUT_OBJECT),          // 近7天PUT请求数
        RECENT_HOURS_READ(MetricTypeEnum.CAPACITY, AcitonEnum.GET_OBJECT),       // 近12h读取量
        RECENT_HOURS_WRITE(MetricTypeEnum.CAPACITY, AcitonEnum.PUT_OBJECT),      // 近12h写入量
        RECENT_HOURS_GET(MetricTypeEnum.REQ_NUM, AcitonEnum.GET_OBJECT),         // 近12h GET请求数
        RECENT_HOURS_PUT(MetricTypeEnum.REQ_NUM, AcitonEnum.PUT_OBJECT),         // 近12h PUT请求数
        DAILY_MAX_READ(MetricTypeEnum.CAPACITY, AcitonEnum.GET_OBJECT),          // 今日最大读取量
        DAILY_MAX_WRITE(MetricTypeEnum.CAPACITY, AcitonEnum.PUT_OBJECT),         // 今日最大写入量
        DAILY_MIN_READ(MetricTypeEnum.CAPACITY, AcitonEnum.GET_OBJECT),          // 今日最小读取量
        DAILY_MIN_WRITE(MetricTypeEnum.CAPACITY, AcitonEnum.PUT_OBJECT);         // 今日最小写入量

        private MetricTypeEnum metricType;

        private AcitonEnum action;

        private QueryTypeEnum(MetricTypeEnum metricType, AcitonEnum action) {
            this.metricType = metricType;
            this.action = action;
        }

        public MetricTypeEnum getMetricType() {
            return metricType;
        }

        public AcitonEnum getAction() {
            return action;
        }
    }

    /**
     * 参数类型
     */
    public static enum MetricTypeEnum {
        CAPACITY,  // 容量
        REQ_NUM    // 请求数
    }

    /**
     * 单个查询指标信息
     * @param bucketId
     * @param type
     * @return
     */
    private long singleQureyMetric(String bucketId, QueryTypeEnum type) {
        // 开始时间
        long startTime = 0L;
        // 结束时间
        long endTime = 0L;
        // 获取当前系统时间
        long currTime = System.currentTimeMillis();

        switch (type) {
            case MONTH_READ:
            case MONTH_WRITE:
            case MONTH_GET:
            case MONTH_PUT:
                startTime = TimeUtils.getMonthStartTime(currTime, Constants.TIME_ZONE);
                endTime = TimeUtils.getMonthEndTime(currTime, Constants.TIME_ZONE);
                break;
            case DAILY_READ:
            case DAILY_WRITE:
            case DAILY_GET:
            case DAILY_PUT:
                startTime = TimeUtils.getDailyStartTime(currTime, Constants.TIME_ZONE);
                endTime = TimeUtils.getDailyEndTime(currTime, Constants.TIME_ZONE);
                break;
            case LAST_DAY_READ:
            case LAST_DAY_WRITE:
            case LAST_DAY_GET:
            case LAST_DAY_PUT:
                startTime = TimeUtils.getDailyStartTime(currTime - Constants.DAY_MILLS, Constants.TIME_ZONE);
                endTime = TimeUtils.getDailyEndTime(currTime - Constants.DAY_MILLS, Constants.TIME_ZONE);
                break;
            case DAILY_MAX_READ:
            case DAILY_MIN_READ:
            case DAILY_MAX_WRITE:
            case DAILY_MIN_WRITE:
                startTime = TimeUtils.getDailyStartTime(currTime, Constants.TIME_ZONE);
                endTime = TimeUtils.getDailyEndTime(currTime, Constants.TIME_ZONE);
                break;
            default:
                logger.error("不支持的指标类型.");
                throw new BusinessException("不支持的指标类型.");
        }

        return commonProcQueryResult(bucketId, type, startTime, endTime);
    }

    /**
     * 批量查询指标信息
     * @param bucketId
     * @param type
     * @return
     */
    private List<TrafficPara> batchQureyMetric(String bucketId, QueryTypeEnum type) {
        List<TrafficPara> trafficParas = null;

        switch (type) {
            case RECENT_DAYS_READ:
            case RECENT_DAYS_WRITE:
            case RECENT_DAYS_GET:
            case RECENT_DAYS_PUT:
                trafficParas = procBatchQueryOfRecentDays(bucketId, type);
                break;
            case RECENT_HOURS_READ:
            case RECENT_HOURS_WRITE:
            case RECENT_HOURS_GET:
            case RECENT_HOURS_PUT:
                trafficParas = procBatchQueryOfRecentHours(bucketId, type);
                break;
            default:
                logger.error("不支持的指标类型.");
                throw new BusinessException("不支持的指标类型.");
        }

        return trafficParas;
    }

    /**
     * 处理批量查询近7日指标信息
     * @param bucketId
     * @param type
     * @return
     */
    private List<TrafficPara> procBatchQueryOfRecentDays(String bucketId, QueryTypeEnum type) {
        // 查询结果
        List<TrafficPara> trafficParas = new ArrayList<>();
        // 获取当前系统时间
        long currTime = System.currentTimeMillis();
        // 当天开始时间
        long dayStartTime = TimeUtils.getDailyStartTime(currTime, Constants.TIME_ZONE);
        // 当天结束时间
        long dayEndTime = TimeUtils.getDailyEndTime(currTime, Constants.TIME_ZONE);

        for (int i = 0; i < Constants.RECENT_DAYS; i++) {
            long startTime = dayStartTime - Constants.DAY_MILLS * (Constants.RECENT_DAYS -1 - i);
            long endTime = dayEndTime - Constants.DAY_MILLS * (Constants.RECENT_DAYS -1 - i);
            long totalReadCapacity = commonProcQueryResult(bucketId, type, startTime, endTime);

            TrafficPara trafficPara = new TrafficPara(startTime, totalReadCapacity, null);
            trafficParas.add(trafficPara);
        }

        return trafficParas;
    }

    /**
     * 处理批量查询近12h指标信息
     * @param bucketId
     * @param type
     * @return
     */
    private List<TrafficPara> procBatchQueryOfRecentHours(String bucketId, QueryTypeEnum type) {
        // 查询结果
        List<TrafficPara> trafficParas = new ArrayList<>();
        // 获取当前系统时间
        long currTime = System.currentTimeMillis();
        // 开始时间;(最近12h，实际有13个坐标值，坐标原点为0)
        long hourStartTime = currTime - Constants.TWENTY_HOURS_MILLS;
        // 结束时间
        long hourEndTime = currTime;

        // 坐标原点无值
        trafficParas.add(new TrafficPara(hourStartTime, 0L, null));

        for (int i = 0; i < Constants.RECENT_HOURS; i++) {
            long startTime = hourStartTime + i * Constants.ONE_HOUR_MILLS;
            long endTime = startTime + Constants.ONE_HOUR_MILLS;
            long totalValue = commonProcQueryResult(bucketId, type, startTime, endTime);

            trafficParas.add(new TrafficPara(endTime, totalValue, null));
        }

        return trafficParas;
    }

    /**
     * 处理分页查询结果
     * @param bucketId
     * @param type
     * @param startTime
     * @param endTime
     * @return
     */
    private long commonProcQueryResult(String bucketId, QueryTypeEnum type, long startTime, long endTime) {
        long totalTraffic = 0L;

        Long retValue;
        MetricTypeEnum metricType = type.metricType;
        switch (metricType) {
            case CAPACITY:
                retValue = procCapacityQuery(bucketId, type, startTime, endTime);
                totalTraffic = retValue == null ? 0L : retValue.longValue();
                break;
            case REQ_NUM:
                retValue = this.bucketAuditDao.getReqNumByActionType(bucketId, type.action.getAction(), startTime, endTime);
                totalTraffic = retValue == null ? 0L : retValue.longValue();
                break;
            default:
                logger.error("不支持的指标类型.");
                throw new BusinessException("不支持的指标类型.");
        }

        return totalTraffic;
    }

    /**
     * 处理存储容量查询结果
     * @param bucketId
     * @param type
     * @param startTime
     * @param endTime
     * @return
     */
    private long procCapacityQuery(String bucketId, QueryTypeEnum type, long startTime, long endTime) {
        long totalTraffic = 0L;

        Long retValue;
        switch (type) {
            case DAILY_MAX_READ:
            case DAILY_MAX_WRITE:
                retValue = this.bucketAuditDao.getMaxCapacityByActionType(
                        bucketId, type.action.getAction(), startTime, endTime);
                totalTraffic = retValue == null ? 0L : retValue.longValue();
                break;
            case DAILY_MIN_WRITE:
            case DAILY_MIN_READ:
                retValue = this.bucketAuditDao.getMinCapacityByActionType(
                        bucketId, type.action.getAction(), startTime, endTime);
                totalTraffic = retValue == null ? 0L : retValue.longValue();
                break;
            default:
                int pageNum = 1;
                Page<BucketAuditDto> bucketAuditDtos = pagedQurey(bucketId, type, pageNum++, startTime, endTime);
                totalTraffic += calTrafficFromList(bucketAuditDtos.getResult());

                int totalPages = bucketAuditDtos.getPages();
                for (; pageNum <= totalPages; pageNum++) {
                    bucketAuditDtos = pagedQurey(bucketId, type, pageNum, startTime, endTime);
                    totalTraffic += calTrafficFromList(bucketAuditDtos.getResult());
                }
        }

        return totalTraffic;
    }

    /**
     * 计算列表中所有对象值的和
     * @param bucketAuditDtos
     * @return
     */
    private long calTrafficFromList(List<BucketAuditDto> bucketAuditDtos) {
        long totalTraffic = 0L;

        for (BucketAuditDto tmpBucketAuditDto : bucketAuditDtos) {
            totalTraffic += tmpBucketAuditDto.getTraffic();
        }

        return totalTraffic;
    }

    /**
     * 根据指定条件，分页获取查询结果
     * @param bucketId
     * @param type
     * @param pageNum
     * @param startTime
     * @param endTime
     * @return
     */
    private Page<BucketAuditDto> pagedQurey(String bucketId, QueryTypeEnum type,
                                            int pageNum, long startTime, long endTime) {
        // step1:设置分页信息
        com.github.pagehelper.PageHelper.startPage(pageNum, Constants.MAX_PAGE_SIZE);

        // step2:根据查询类型获取查询结果
        Page<BucketAuditDto> bucketAuditDtos = this.bucketAuditDao.getCapacityByActionType(bucketId,
                type.getAction().getAction(), startTime, endTime);

        return bucketAuditDtos;
    }

    /**
     * 将TrafficPara类型转换为ReqPara
     * @param trafficParas
     * @return
     */
    private List<ReqPara> chgTraffic2ReqPara(List<TrafficPara> trafficParas) {
        List<ReqPara> reqParas = new ArrayList<>();
        for (TrafficPara para : trafficParas) {
            ReqPara tmpReqPara = new ReqPara(para.getStamp(), (long)para.getMeasuredValue(), Constants.NUM_UNIT);

            reqParas.add(tmpReqPara);
        }

        return reqParas;
    }

    /**
     * 从查询结果中解析横坐标（时间戳信息）
     * @param trafficParas
     * @return
     */
    private  List<Long> parseStampFromTrafficParas(List<TrafficPara> trafficParas) {
        List<Long> horizontalList = new ArrayList<>();

        for (TrafficPara para : trafficParas) {
            horizontalList.add(para.getStamp());
        }

        return horizontalList;
    }

    /**
     * 从查询结果中解析横坐标（时间戳信息）
     * @param trafficParas
     * @return
     */
    private  List<Long> parseStampFromReqParas(List<ReqPara> trafficParas) {
        List<Long> horizontalList = new ArrayList<>();

        for (ReqPara para : trafficParas) {
            horizontalList.add(para.getStamp());
        }

        return horizontalList;
    }

    /**
     * 动态调整纵轴坐标（存储容量）
     * @param paras
     * @return
     */
    private List<TrafficPara> adjustVerticalOfTrafficPara(List<TrafficPara> paras) {
        List<TrafficPara> trafficParas = new ArrayList<>();
        DecimalFormat decimalFormat = new DecimalFormat(Constants.DATA_FORMAT);

        // step1:获取列表中最大值、最小值。二者相等，直接返回默认纵坐标
        long maxValue = getMaxParaFromTrafficParas(paras);
        long minValue = 0;
        if (maxValue == minValue) {
            for (int i = 0; i < Constants.NUM_FIVE; i++) {
                double tmpValue = Constants.DEFAULT_VERTICAL_STEP * i;
                TrafficPara para = new TrafficPara(0L, Double.parseDouble(decimalFormat.format(tmpValue)), Constants.KB_UNIT);
                trafficParas.add(para);
            }

            return trafficParas;
        }

        // step2:列表中有值，调用自适应算法，获取调整后的总坐标
        // step2-1:计算步长
        double grossStep = (maxValue - minValue) / Constants.DEFAULT_TICK_NUM;
        double step = Math.pow(Constants.NUM_TEN, Math.floor(Math.log10(grossStep)));
        if(grossStep < Constants.NUM_TWO * step) {
            step = step * 1;
        }else if (grossStep >= Constants.NUM_TWO * step && grossStep < Constants.NUM_FIVE * step ) {
            step = step * Constants.NUM_TWO;
        } else {
            step = step * Constants.NUM_FIVE;
        }

        //step2-2:重新计算刻度数
        int tickNum = (int) (Math.ceil(maxValue / step) - Math.floor(minValue / step));

        //step2-3:计算步长对应数据大小、单位
        TrafficPara stepPara = MathUtils.storageUnitProcess2((long)step);

        // step2-4:设置刻度值
        for (int i = 0; i < tickNum; i++) {
            TrafficPara para = new TrafficPara(0L, stepPara.getMeasuredValue() * i, stepPara.getUnit());
            trafficParas.add(para);
        }

        return trafficParas;
    }

    /**
     * 动态调整纵轴坐标（请求数）
     * @param paras
     * @return
     */
    private List<ReqPara> adjustVerticalOfReqPara(List<ReqPara> paras) {
        List<ReqPara> reqParas = new ArrayList<>();

        // step1:列表中无值，默认返回一个横坐标值
        if (reqParas.size() == 0) {
            for (int i = 0; i < Constants.NUM_FIVE; i++) {
                ReqPara para = new ReqPara(0L, i, Constants.NUM_UNIT);
                reqParas.add(para);
            }

            return reqParas;
        }

        // step2:列表中有值，调用自适应算法，获取调整后的总坐标
        // step2-1:获取列表中最大值、最小值
        long maxValue = getMaxParaFromReqParas(paras);
        long minValue = 0;

        // step2-2:计算步长
        double grossStep = (maxValue - minValue) / Constants.DEFAULT_VERTICAL_STEP;
        double step = Math.pow(Constants.NUM_TEN, Math.floor(Math.log10(grossStep)));
        if(grossStep < Constants.NUM_TWO * step) {
            step = step * 1;
        }else if (grossStep >= Constants.NUM_TWO * step && grossStep < Constants.NUM_FIVE * step ) {
            step = step * Constants.NUM_TWO;
        } else {
            step = step * Constants.NUM_FIVE;
        }

        //step2-3:重新计算刻度数
        int tickNum = (int) (Math.ceil(maxValue / step) - Math.floor(minValue / step));

        // step2-4:设置刻度值
        for (int i = 0; i < tickNum; i++) {
            ReqPara para = new ReqPara(0L, (long)step * i, Constants.NUM_UNIT);
            reqParas.add(para);
        }

        return reqParas;
    }

    /**
     * 从列表中获取最大参数值（measuredValue最大）
     * @param trafficParas
     * @return
     */
    private long getMaxParaFromTrafficParas (List<TrafficPara> trafficParas) {
        long maxValue = 0L;

        for (TrafficPara para : trafficParas) {
            long tmpValue = (long)para.getMeasuredValue();
            if (tmpValue > maxValue) {
                maxValue = tmpValue;
            }
        }

        return maxValue;
    }

    /**
     * 从列表中获取最小参数值（measuredValue最小）
     * @param trafficParas
     * @return
     */
    private long getMinParaFromTrafficParas (List<TrafficPara> trafficParas) {
        long minValue = 0L;

        for (TrafficPara para : trafficParas) {
            long tmpValue = (long)para.getMeasuredValue();
            if (tmpValue < minValue) {
                minValue = tmpValue;
            }
        }

        return minValue;
    }

    /**
     * 从列表中获取最大参数值（measuredValue最大）
     * @param trafficParas
     * @return
     */
    private long getMaxParaFromReqParas (List<ReqPara> trafficParas) {
        long maxValue = 0L;

        for (ReqPara para : trafficParas) {
            long tmpValue = (long)para.getMeasuredValue();
            if (tmpValue > maxValue) {
                maxValue = tmpValue;
            }
        }

        return maxValue;
    }

    /**
     * 从列表中获取最小参数值（measuredValue最小）
     * @param trafficParas
     * @return
     */
    private long getMinParaFromReqParas (List<ReqPara> trafficParas) {
        long minValue = 0L;

        for (ReqPara para : trafficParas) {
            long tmpValue = (long)para.getMeasuredValue();
            if (tmpValue < minValue) {
                minValue = tmpValue;
            }
        }

        return minValue;
    }

    /**
     * 根据纵轴坐标单位，处理值
     * @param srcParas
     * @param referParas
     */
    private void procCapacityWithUnit(List<TrafficPara> srcParas, List<TrafficPara> referParas) {
        TrafficPara referPara = referParas.get(0);
        String referUnit = referPara.getUnit();

        long referValue = 0L;
        switch (referUnit) {
            case Constants.BYTE_UNIT:
                referValue = 1;
                break;
            case Constants.KB_UNIT:
                referValue = Constants.KB;
                break;
            case Constants.MB_UNIT:
                referValue = Constants.MB;
                break;
            case Constants.GB_UNIT:
                referValue = Constants.GB;
                break;
            default:
                referValue = Constants.TB;

        }

        for (TrafficPara para : srcParas) {
            DecimalFormat decimalFormat = new DecimalFormat(Constants.DATA_FORMAT);
            double finalValue = Double.parseDouble(decimalFormat.format(para.getMeasuredValue() / (double)referValue));
            para.setMeasuredValue(finalValue);
        }
    }
}
